


// 2d array utilities function

getXYInArrayOfWH = function( _Array, _X, _Y, _W, _H ){
    
    if( _X >= 0 && _X < _W && _Y >=0 && _Y < _H )
    {
        return _Array[ _X + (_Y*_W) ];
    }
    
    return 0;// _Array[ (_X%_W) + ((_Y%_H)*_W) ];
};
addNumberAtXYInArrayOfWH = function( _Number, _Array, _X, _Y, _W, _H ){

    if( _X >= 0 && _X < _W && _Y >=0 && _Y < _H )
    {
        _Array[ _X + (_Y*_W) ] += _Number;
    }
    /*
    else
    {
        _Array[ (_X%_W) + ((_Y%_H)*_W) ] += _Number;
    }
    */
};
setNumberAtXYInArrayOfWH = function( _Number, _Array, _X, _Y, _W, _H ){

    if( _X >= 0 && _X < _W && _Y >=0 && _Y < _H )
    {
        _Array[ _X + (_Y*_W) ] = _Number;
    }
    /*
    else
    {
        _Array[ (_X%_W) + ((_Y%_H)*_W) ] = _Number;
    }
    */
};




// marching square object helper

function marchingSquare (_w,_h,_coef, _treshold_MIN, _treshold_MAX, _Equation){
    return new _marchingSquare(_w,_h,_coef, _treshold_MIN, _treshold_MAX, _Equation);
};

function _marchingSquare (_w,_h,_coef, _treshold_MIN, _treshold_MAX, _Equation) {
    
    this._Equation = _Equation;
    
    this._treshold_MIN = _treshold_MIN;
    this._treshold_MAX = _treshold_MAX;
    
    this._coef = _coef;
    this._w = _w / _coef;
    this._h = _h / _coef;
    this._off = _coef / 2;
    
    this.Data = [];
    this.Data.length = this._w*this._h;
    
    this.Data2 = [];
    this.Data2.length = (this._w-1)*(this._h-1);
    
    this.DataBorders = [];
    this.WorldSpaceDataBorders = [];
    // function
    
    this.clear = function(){
        for(var i=0; i<this.Data.length; i++)
            this.Data[i] = 0;
            
        for(var i=0; i<this.Data2.length; i++)
            this.Data2[i] = 0;
    };
    
    this.resolve = function(){
        this.clear();
        
        // set row data
        for(var x=0; x<this._w; x++){
            for(var y=0; y<this._h; y++){
                
                setNumberAtXYInArrayOfWH( this._Equation( x*this._coef + this._off, y*this._coef + this._off ),
                                            this.Data,     x,y, this._w, this._h );
            }
        }
        
        // pure marching square alogrithm 
        resolveMarchingSquare_isoline( this.Data, this._w, this._h, this._treshold_MIN, this._treshold_MAX, this.Data2);
    
    
        this.DataBorders = resolveBorder(this.Data2,this._w-1,this._h-1);
        this.convertDataBorderToWorldSpace();
    };
    
    this.convertDataBorderToWorldSpace = function(){
        
        this.WorldSpaceDataBorders.length = 0;
        
        for(var i=0; i<this.DataBorders.length; i++)
        {
            var vertices = [];
            
            for(var j=0; j<this.DataBorders[i].length; j++)
            {
                var x = this.DataBorders[i][j]._x;
                var y = this.DataBorders[i][j]._y;
                
                var Vx = x*this._coef + this._off;
                var Vy = y*this._coef + this._off;
                var off = this._off;
                var coef = this._coef;
            
                var value = this.DataBorders[i][j]._value;
                
                var back = vertices.length > 0 ? vertices[ vertices.length - 1] : null;
                var TestWithBack = function( p ){
                    return ((back.x == p.x) && (back.y == p.y));
                };
                var TestIfInvertNeeded = function(_p1,_p2){
                    if( vertices.length == 2 && 
                        ! TestWithBack(_p1)   &&
                        ! TestWithBack(_p2) ){
                        var tmp = vertices[0];
                        vertices[0] = vertices[1];
                        vertices[1] = tmp;
                        
                        back = vertices.length > 0 ? vertices[ vertices.length - 1] : null;
                    }
                };
                
                switch( value )
                {
                case 0: /*nothing*/ break;
                
                case 1: 
                case 14: 
                    var p1 =     cc.p(Vx,Vy+off)                ;
                    var p2 =      cc.p(Vx+off,Vy+coef)        ;
                    
                    TestIfInvertNeeded(p1,p2);
                    
                    if(vertices.length == 0){
                        vertices.push( p1 );
                        vertices.push( p2 );
                    } else if( TestWithBack(p1) ){
                        vertices.push( p2 );
                    }else if( TestWithBack(p2) ){
                        vertices.push( p1 );
                    }
                    break;
                    
                case 2: 
                case 13: 
                    var p1 =     cc.p(Vx+off,Vy+coef)    ;
                    var p2 =      cc.p(Vx+coef,Vy+off)    ;
                    
                    TestIfInvertNeeded(p1,p2);
                    
                    if(vertices.length == 0){
                        vertices.push( p1 );
                        vertices.push( p2 );
                    } else if( TestWithBack(p1) ){
                        vertices.push( p2 );
                    }else if( TestWithBack(p2) ){
                        vertices.push( p1 );
                    }
                    break;
                    
                case 3: 
                case 12: 
                    var p1 =     cc.p(Vx,Vy+off)         ;
                    var p2 =      cc.p(Vx+coef,Vy+off)    ;
                    
                    TestIfInvertNeeded(p1,p2);
                    
                    if(vertices.length == 0){
                        vertices.push( p1 );
                        vertices.push( p2 );
                    } else if( TestWithBack(p1) ){
                        vertices.push( p2 );
                    }else if( TestWithBack(p2) ){
                        vertices.push( p1 );
                    }
                    break;
                    
                case 4: 
                case 11: 
                    var p1 =     cc.p(Vx+off,Vy)         ;
                    var p2 =      cc.p(Vx+coef,Vy+off)    ;
                    
                    TestIfInvertNeeded(p1,p2);
                    
                    if(vertices.length == 0){
                        vertices.push( p1 );
                        vertices.push( p2 );
                    } else if( TestWithBack(p1) ){
                        vertices.push( p2 );
                    }else if( TestWithBack(p2) ){
                        vertices.push( p1 );
                    }
                    break;
                    
                case 6: 
                case 9: 
                    var p1 =     cc.p(Vx+off,Vy)            ;
                    var p2 =      cc.p(Vx+off,Vy+coef)    ;
                    
                    TestIfInvertNeeded(p1,p2);
                    
                    if(vertices.length == 0){
                        vertices.push( p1 );
                        vertices.push( p2 );
                    } else if( TestWithBack(p1) ){
                        vertices.push( p2 );
                    }else if( TestWithBack(p2) ){
                        vertices.push( p1 );
                    }
                    break;
                    
                case 7: 
                case 8: 
                    var p1 =     cc.p(Vx,Vy+off)        ;
                    var p2 =      cc.p(Vx+off,Vy)        ;
                    
                    TestIfInvertNeeded(p1,p2);
                    
                    if(vertices.length == 0){
                        vertices.push( p1 );
                        vertices.push( p2 );
                    } else if( TestWithBack(p1) ){
                        vertices.push( p2 );
                    }else if( TestWithBack(p2) ){
                        vertices.push( p1 );
                    }
                    break;
                    
                    
                case 5: 
                    var p1 =     cc.p(Vx,Vy+off)        ;
                    var p2 =      cc.p(Vx+off,Vy)        ;
                    
                    var p3 =     cc.p(Vx+off,Vy+coef)        ;
                    var p4 =      cc.p(Vx+coef,Vy+off)        ;
                
                
                    TestIfInvertNeeded(p1,p2);
                    TestIfInvertNeeded(p3,p4);
                    
                
                    if(vertices.length == 0){
                        // OMG !!!!
                    } else if( TestWithBack(p1) ){
                        vertices.push( p2 );
                    } else if( TestWithBack(p2) ){
                        vertices.push( p1 );
                    } else if( TestWithBack(p3) ){
                        vertices.push( p4 );
                    } else if( TestWithBack(p4) ){
                        vertices.push( p3 );
                    }
                    break;
                
                
                case 10: 
                    var p1 =     cc.p(Vx+off,Vy)                ;
                    var p2 =      cc.p(Vx+coef,Vy+off)        ;
                    
                    var p3 =     cc.p(Vx,Vy+off)                ;
                    var p4 =      cc.p(Vx+off,Vy+coef)        ;
                
                
                    TestIfInvertNeeded(p1,p2);
                    TestIfInvertNeeded(p3,p4);
                    
                
                    if(vertices.length == 0){
                        // OMG !!!!
                    } else if( TestWithBack(p1) ){
                        vertices.push( p2 );
                    } else if( TestWithBack(p2) ){
                        vertices.push( p1 );
                    } else if( TestWithBack(p3) ){
                        vertices.push( p4 );
                    } else if( TestWithBack(p4) ){
                        vertices.push( p3 );
                    }
                    break;

                case 15: 
                    /* nothing */
                    break;
                }
            }
        
        
            this.WorldSpaceDataBorders[i] = vertices;
        }
    };
    
    this.debugDraw = function(ctx){
        
        var drawAll = true;
        
        var r = new cc.Color3B(255,0,0);
        var g = new cc.Color3B(0,255,0);
        var b = new cc.Color3B(0,0,255);
    
        for(var x=0; x<this._w; x++){
            for(var y=0; y<this._h; y++){
            
                var Vx = (x*this._coef) + this._off;
                var Vy = (y*this._coef) + this._off;
                var coef = this._coef;
                
                switch( getXYInArrayOfWH(this.Data, x,y, this._w, this._h ) )
                {
                case 0: 
                    cc.drawingUtil.drawColorBall(null,cc.p(Vx,Vy),2, b); 
                    break;
                    
                case 1: 
                    cc.drawingUtil.drawColorBall(null,cc.p(Vx,Vy),2, g); 
                    break;
                    
                case 2: 
                    if(drawAll){
                        cc.drawingUtil.drawColorBall(null,cc.p(Vx,Vy),2, r); 
                    }
                    break;
                }
                
            }
        }
    };
    
    this.draw = function(ctx){
        var useBatch = false;
        
        if(useBatch){
            this.drawDataBorder(ctx,true);
            this.drawDataBorder(ctx);
        } else {
            this.drawFilled(ctx);
        }   this.drawBorder(ctx);

        //this.debugDraw(ctx);
    };
    
    this.drawBorder = function(ctx){
    
        for(var x=0; x<this._w-1; x++){
            for(var y=0; y<this._h-1; y++){
        
                var Vx = x*this._coef + this._off;
                var Vy = y*this._coef + this._off;
                var off = this._off;
                var coef = this._coef;
                
                switch( getXYInArrayOfWH(this.Data2, x,y, this._w-1, this._h-1 ) )
                { // cc.drawingUtil.drawLine( cc.p(Vx,Vy), cc.p(Vx,Vy) );
                case 0: /*nothing*/ break;
                case 1: 
                    cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy+coef) );
                    break;
                    
                case 2: 
                    cc.drawingUtil.drawLine( cc.p(Vx+off,Vy+coef), cc.p(Vx+coef,Vy+off) );
                    break;
                    
                case 3: 
                    cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+coef,Vy+off) );
                    break;
                    
                case 4: 
                    cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+coef,Vy+off) );
                    break;
                    
                case 5: 
                    cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy) );
                    cc.drawingUtil.drawLine( cc.p(Vx+off,Vy+coef), cc.p(Vx+coef,Vy+off) );
                    break;
                
                case 6: 
                    cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+off,Vy+coef) );
                    break;
                    
                case 7: 
                    cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy) );
                    break;
                    
                case 8: 
                    cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy) );
                    break;
                    
                case 9: 
                    cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+off,Vy+coef) );
                    break;
                
                case 10: 
                    cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+coef,Vy+off) );
                    cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy+coef) );
                    break;
                    
                case 11: 
                    cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+coef,Vy+off) );
                    break;
                    
                case 12: 
                    cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+coef,Vy+off) );
                    break;
                    
                case 13: 
                    cc.drawingUtil.drawLine( cc.p(Vx+off,Vy+coef), cc.p(Vx+coef,Vy+off) );
                    break;
                
                case 14: 
                    cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy+coef) );
                    break;
                    
                case 15: 
                    /* nothing */
                    break;
                }
            }
        }
    };
    
    
    this.drawFilled = function(ctx){

        var vertices = new Array(5);
        
        for(var x=0; x<this._w-1; x++){
            for(var y=0; y<this._h-1; y++){
        
                var Vx = (x*this._coef) + this._off;
                var Vy = (y*this._coef) + this._off;
                var off = this._off;
                var coef = this._coef;
            
                switch( getXYInArrayOfWH(this.Data2, x,y, this._w-1, this._h-1 ) )
                { // cc.drawingUtil.drawLine( cc.p(Vx,Vy), cc.p(Vx,Vy) );
                case 0: 
                    vertices[0] = ( cc.p(Vx,Vy) );
                    vertices[1] = ( cc.p(Vx+coef,Vy) );
                    vertices[2] = ( cc.p(Vx+coef,Vy+coef) );
                    vertices[3] = ( cc.p(Vx,Vy+coef) );
                    
                    vertices.length = 4;
                    cc.drawingUtil.drawPoly(vertices, 4, true, true);
                    break;
                case 1: 
                    //cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy+coef) );
                    vertices[0] = ( cc.p(Vx,Vy+off) );
                    vertices[1] = ( cc.p(Vx+off,Vy+coef) );
                    vertices[2] = ( cc.p(Vx+coef,Vy+coef) );
                    vertices[3] = ( cc.p(Vx+coef,Vy) );
                    vertices[4] = ( cc.p(Vx,Vy) );
                    
                    vertices.length = 5;
                    cc.drawingUtil.drawPoly(vertices, 5, true, true);
                    break;
                    
                case 2: 
                    //cc.drawingUtil.drawLine( cc.p(Vx+off,Vy+coef), cc.p(Vx+coef,Vy+off) );
                    vertices[0] = ( cc.p(Vx+off,Vy+coef) );
                    vertices[1] = ( cc.p(Vx+coef,Vy+off) );
                    vertices[2] = ( cc.p(Vx+coef,Vy) );
                    vertices[3] = ( cc.p(Vx,Vy) );
                    vertices[4] = ( cc.p(Vx,Vy+coef) );
                    
                    vertices.length = 5;
                    cc.drawingUtil.drawPoly(vertices, 5, true, true);
                    break;
                    
                case 3: 
                    //cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+coef,Vy+off) );
                    vertices[0] = ( cc.p(Vx,Vy+off) );
                    vertices[1] = ( cc.p(Vx+coef,Vy+off) );
                    vertices[2] = ( cc.p(Vx+coef,Vy) );
                    vertices[3] = ( cc.p(Vx,Vy) );
                    
                    vertices.length = 4;
                    cc.drawingUtil.drawPoly(vertices, 4, true, true);
                    break;
                    
                case 4: 
                    //cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+coef,Vy+off) );
                    vertices[0] = ( cc.p(Vx+off,Vy) );
                    vertices[1] = ( cc.p(Vx+coef,Vy+off) );
                    vertices[2] = ( cc.p(Vx+coef,Vy+coef) );
                    vertices[3] = ( cc.p(Vx,Vy+coef) );
                    vertices[4] = ( cc.p(Vx,Vy) );
                    
                    vertices.length = 5;
                    cc.drawingUtil.drawPoly(vertices, 5, true, true);
                    break;
                    
                case 5: 
                    //cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy) );
                    //cc.drawingUtil.drawLine( cc.p(Vx+off,Vy+coef), cc.p(Vx+coef,Vy+off) );
                    vertices[0] = ( cc.p(Vx,Vy+off) );
                    vertices[1] = ( cc.p(Vx+off,Vy) );
                    vertices[2] = ( cc.p(Vx,Vy) );
                    
                    vertices.length = 3;
                    cc.drawingUtil.drawPoly(vertices, 3, true, true);
                    
                    vertices[0] = ( cc.p(Vx+off,Vy+coef) );
                    vertices[1] = ( cc.p(Vx+coef,Vy+off) );
                    vertices[2] = ( cc.p(Vx+coef,Vy+coef) );
                    
                    vertices.length = 3;
                    cc.drawingUtil.drawPoly(vertices, 3, true, true);
                    break;
                
                case 6: 
                    //cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+off,Vy+coef) );
                    vertices[0] = ( cc.p(Vx+off,Vy) );
                    vertices[1] = ( cc.p(Vx+off,Vy+coef) );
                    vertices[2] = ( cc.p(Vx,Vy+coef) );
                    vertices[3] = ( cc.p(Vx,Vy) );
                    
                    vertices.length = 4;
                    cc.drawingUtil.drawPoly(vertices, 4, true, true);
                    break;
                    
                case 7: 
                    //cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy) );
                    vertices[0] = ( cc.p(Vx,Vy+off) );
                    vertices[1] = ( cc.p(Vx+off,Vy) );
                    vertices[2] = ( cc.p(Vx,Vy) );
                    
                    vertices.length = 3;
                    cc.drawingUtil.drawPoly(vertices, 3, true, true);
                    break;
                    
                case 8: 
                    //cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy) );
                    vertices[0] = ( cc.p(Vx,Vy+off) );
                    vertices[1] = ( cc.p(Vx+off,Vy) );
                    vertices[2] = ( cc.p(Vx+coef,Vy) );
                    vertices[3] = ( cc.p(Vx+coef,Vy+coef) );
                    vertices[4] = ( cc.p(Vx,Vy+coef) );
                    
                    vertices.length = 5;
                    cc.drawingUtil.drawPoly(vertices, 5, true, true);
                    break;
                    
                case 9: 
                    //cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+off,Vy+coef) );
                    vertices[0] = ( cc.p(Vx+off,Vy) );
                    vertices[1] = ( cc.p(Vx+off,Vy+coef) );
                    vertices[2] = ( cc.p(Vx+coef,Vy+coef) );
                    vertices[3] = ( cc.p(Vx+coef,Vy) );
                    
                    vertices.length = 4;
                    cc.drawingUtil.drawPoly(vertices, 4, true, true);
                    break;
                
                case 10: 
                    //cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+coef,Vy+off) );
                    //cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy+coef) );
                    vertices[0] = ( cc.p(Vx+off,Vy) );
                    vertices[1] = ( cc.p(Vx+coef,Vy+off) );
                    vertices[2] = ( cc.p(Vx+coef,Vy) );
                    
                    vertices.length = 3;
                    cc.drawingUtil.drawPoly(vertices, 3, true, true);
                    
                    vertices[0] = ( cc.p(Vx,Vy+off) );
                    vertices[1] = ( cc.p(Vx+off,Vy+coef) );
                    vertices[2] = ( cc.p(Vx,Vy+coef) );
                    
                    vertices.length = 3;
                    cc.drawingUtil.drawPoly(vertices, 3, true, true);
                    break;
                    
                case 11: 
                    //cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+coef,Vy+off) );
                    vertices[0] = ( cc.p(Vx+off,Vy) );
                    vertices[1] = ( cc.p(Vx+coef,Vy+off) );
                    vertices[2] = ( cc.p(Vx+coef,Vy) );
                    
                    vertices.length = 3;
                    cc.drawingUtil.drawPoly(vertices, 3, true, true);
                    break;
                    
                case 12: 
                    //cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+coef,Vy+off) );
                    vertices[0] = ( cc.p(Vx,Vy+off) );
                    vertices[1] = ( cc.p(Vx+coef,Vy+off) );
                    vertices[2] = ( cc.p(Vx+coef,Vy+coef) );
                    vertices[3] = ( cc.p(Vx,Vy+coef) );
                    
                    vertices.length = 4;
                    cc.drawingUtil.drawPoly(vertices, 4, true, true);
                    break;
                    
                case 13: 
                    //cc.drawingUtil.drawLine( cc.p(Vx+off,Vy+coef), cc.p(Vx+coef,Vy+off) );
                    vertices[0] = ( cc.p(Vx+off,Vy+coef) );
                    vertices[1] = ( cc.p(Vx+coef,Vy+off) );
                    vertices[2] = ( cc.p(Vx+coef,Vy+coef) );
                    
                    vertices.length = 3;
                    cc.drawingUtil.drawPoly(vertices, 3, true, true);
                    break;
                
                case 14: 
                    //cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy+coef) );
                    vertices[0] = ( cc.p(Vx,Vy+off) );
                    vertices[1] = ( cc.p(Vx+off,Vy+coef) );
                    vertices[2] = ( cc.p(Vx,Vy+coef) );
                    
                    vertices.length = 3;
                    cc.drawingUtil.drawPoly(vertices, 3, true, true);
                    break;
                    
                case 15: 
                    /* nothing */
                    break;
                }
            }
        }
    };


    this.drawDataBorder = function(ctx, filled){
        if(filled == null){
            filled = false;
        }
        
        for(var i=0; i < this.WorldSpaceDataBorders.length; i++)
        {
            if( this.WorldSpaceDataBorders[i].length > 2){
                ctx.fillStyle = "rgba(0,1,1, 1)";
                ctx.lineWidth = 4;
                cc.drawingUtil.drawPoly(this.WorldSpaceDataBorders[i], this.WorldSpaceDataBorders[i].length, true, filled);
            }
        }
    };
};



// pure marching square alogrithm 

applyThreshold = function( _rowData, _Threshold_MIN, _Threshold_MAX ){
    // filter
    for(var i=0; i< _rowData.length; i++)
    {
        if( _rowData[i] < _Threshold_MIN )
            _rowData[i] = 2;
        else if(  _rowData[i] > _Threshold_MAX )
            _rowData[i] = 0;
        else
            _rowData[i] = 1;
    }
};

resolveMarchingSquare_isoline = function( _rowData, _W, _H, _Threshold_MIN, _Threshold_MAX, __outputData ){

    applyThreshold( _rowData, _Threshold_MIN, _Threshold_MAX );
    var _outputData = (__outputData == null) ? new Array( (_W-1) * (_H-1) ) : __outputData;
    _outputData.length = (_W-1) * (_H-1);
    
    
    // set Data 2
    for(var x=0; x<_W-1; x++){
        for(var y=0; y<_H-1; y++){
            
            if( getXYInArrayOfWH(_rowData,        x,y,         _W,_H) == 2 )
                addNumberAtXYInArrayOfWH(8,         _outputData, x, y, _W-1, _H-1);
                
            if( getXYInArrayOfWH(_rowData,        x+1,y,         _W,_H) == 2 )
                addNumberAtXYInArrayOfWH(4,         _outputData, x, y, _W-1, _H-1);
                
            if( getXYInArrayOfWH(_rowData,        x+1,y+1,     _W,_H) == 2 )
                addNumberAtXYInArrayOfWH(2,         _outputData, x, y, _W-1, _H-1);
                
            if( getXYInArrayOfWH(_rowData,        x,y+1,         _W,_H) == 2 )
                addNumberAtXYInArrayOfWH(1,         _outputData, x, y, _W-1, _H-1);
        }
    }
    
    
    return _outputData;
};
    

PopObjectAt_FindAtXY = function( _Array, _x, _y ){
    for(var i=0;i<_Array.length; i++){
        if( _Array[i]._x == _x && _Array[i]._y == _y ){
            var tmp = _Array[i];
            _Array[i] = _Array[0];
            _Array.shift();
            return tmp;
        }
    }
    return null;
};
    
resolveBorder = function( _organizedData, _W, _H ){
    
    var _borderCase = [];
    
    // push all border in one array
    for(var x=0; x<_W-1; x++){
        for(var y=0; y<_H-1; y++){
            
            var value = getXYInArrayOfWH( _organizedData, x, y, _W, _H );
            
            if( value != 0 && value != 15 ){
                _borderCase.push( {_value:value, _x:x, _y:y} );
            }
        }
    }

    var _borders = [];
    while( _borderCase.length > 0 )
    {
        var ActualCase = _borderCase[0];
        var PrevCase = _borderCase[0];
        _borderCase.shift();
        
        var newBorder = [];
        newBorder.push( ActualCase );
        
        var clockwiseEnd = false;
        while(!clockwiseEnd){
            clockwiseEnd = true;
            
            var dx = ActualCase._x - PrevCase._x;
            var dy = ActualCase._y - PrevCase._y; 
            
            if(dx == 0 && dy == 0){
                dx = 1; dy=-1;
                if( ActualCase._value == 4 || ActualCase._value == 11 ){
                    dx = -1; dy=-1;
                }
            }
            
            
            var toRight = function(){ return (dx >= 1); };
            var toLeft = function(){ return (dx <= -1); };
            var toUp = function(){ return (dy <= -1); };
            var toDown = function(){ return (dy >= 1); };
            
            
            
            // GO DOWN
            if( (ActualCase._value == 1     &&     toRight()    ) ||
                (ActualCase._value == 2     &&     toLeft()    ) ||
                (ActualCase._value == 13     &&     toLeft()    ) ||
                (ActualCase._value == 6     &&     toDown()    ) ||
                (ActualCase._value == 9     &&     toDown()    ) ||
                (ActualCase._value == 14     &&     toRight()    )
            ){ 
                var obj = PopObjectAt_FindAtXY(_borderCase, ActualCase._x, ActualCase._y+1 );
                if( obj != null ){
                    newBorder.push( obj );
                    PrevCase = ActualCase;
                    ActualCase = obj;
                    clockwiseEnd = false;
                }
            } // GO UP
            else if(     (ActualCase._value == 7     &&     toRight()    ) ||
                        (ActualCase._value == 4     &&     toLeft()    ) ||
                        (ActualCase._value == 11     &&     toLeft()    ) ||
                        (ActualCase._value == 6     &&     toUp()        ) ||
                        (ActualCase._value == 9     &&     toUp()        ) ||
                        (ActualCase._value == 8     &&     toRight()    ) 
            ){ 
                var obj = PopObjectAt_FindAtXY(_borderCase, ActualCase._x, ActualCase._y-1 );
                if( obj != null ){
                    newBorder.push( obj );
                    PrevCase = ActualCase;
                    ActualCase = obj;
                    clockwiseEnd = false;
                }
            } // GO RIGHT
            else if(     (ActualCase._value == 2     &&     toUp()        ) ||
                        (ActualCase._value == 3     &&     toRight()    ) ||
                        (ActualCase._value == 4     &&     toDown()    ) ||
                        (ActualCase._value == 11     &&     toDown()    ) ||
                        (ActualCase._value == 12     &&     toRight()    ) ||
                        (ActualCase._value == 13     &&     toUp()        )
            ){ 
                var obj = PopObjectAt_FindAtXY(_borderCase, ActualCase._x+1, ActualCase._y );
                if( obj != null ){
                    newBorder.push( obj );
                    PrevCase = ActualCase;
                    ActualCase = obj;
                    clockwiseEnd = false;
                }
            } // GO LEFT
            else if(     (ActualCase._value == 1     &&     toUp()        ) ||
                        (ActualCase._value == 3     &&     toLeft()    ) ||
                        (ActualCase._value == 7     &&     toDown()    ) ||
                        (ActualCase._value == 8     &&     toDown()    ) ||
                        (ActualCase._value == 12     &&     toLeft()    ) ||
                        (ActualCase._value == 14     &&     toUp()        )
            ){ 
                var obj = PopObjectAt_FindAtXY(_borderCase, ActualCase._x-1, ActualCase._y );
                if( obj != null ){
                    newBorder.push( obj );
                    PrevCase = ActualCase;
                    ActualCase = obj;
                    clockwiseEnd = false;
                }
            }  // SPECIAL CASE //
            else if( (ActualCase._value == 5     &&     toDown()    ) ){ 
                // GO LEFT
                var obj = PopObjectAt_FindAtXY(_borderCase, ActualCase._x-1, ActualCase._y );
                if( obj != null ){
                    newBorder.push( obj );
                    PrevCase = ActualCase;
                    ActualCase = obj;
                    clockwiseEnd = false;
                    
                    // re push case
                    _borderCase.push( PrevCase );
                }
            } else if( (ActualCase._value == 5     &&     toUp()    ) ){ 
                // GO RIGHT
                var obj = PopObjectAt_FindAtXY(_borderCase, ActualCase._x+1, ActualCase._y );
                if( obj != null ){
                    newBorder.push( obj );
                    PrevCase = ActualCase;
                    ActualCase = obj;
                    clockwiseEnd = false;
                    
                    // re push case
                    _borderCase.push( PrevCase );
                }
            } else if( (ActualCase._value == 5     &&     toRight()    ) ){ 
                // GO UP
                var obj = PopObjectAt_FindAtXY(_borderCase, ActualCase._x, ActualCase._y-1 );
                if( obj != null ){
                    newBorder.push( obj );
                    PrevCase = ActualCase;
                    ActualCase = obj;
                    clockwiseEnd = false;
                    
                    // re push case
                    _borderCase.push( PrevCase );
                }
            } else if( (ActualCase._value == 5     &&     toLeft()    ) ){ 
                // GO DOWN
                var obj = PopObjectAt_FindAtXY(_borderCase, ActualCase._x, ActualCase._y+1 );
                if( obj != null ){
                    newBorder.push( obj );
                    PrevCase = ActualCase;
                    ActualCase = obj;
                    clockwiseEnd = false;
                    
                    // re push case
                    _borderCase.push( PrevCase );
                }
            } else if( (ActualCase._value == 10     &&     toLeft()    ) ){ 
                // GO UP
                var obj = PopObjectAt_FindAtXY(_borderCase, ActualCase._x, ActualCase._y-1 );
                if( obj != null ){
                    newBorder.push( obj );
                    PrevCase = ActualCase;
                    ActualCase = obj;
                    clockwiseEnd = false;
                    
                    // re push case
                    _borderCase.push( PrevCase );
                }
            } else if( (ActualCase._value == 10     &&     toRight()    ) ){ 
                // GO DOWN
                var obj = PopObjectAt_FindAtXY(_borderCase, ActualCase._x, ActualCase._y+1 );
                if( obj != null ){
                    newBorder.push( obj );
                    PrevCase = ActualCase;
                    ActualCase = obj;
                    clockwiseEnd = false;
                    
                    // re push case
                    _borderCase.push( PrevCase );
                }
            } else if( (ActualCase._value == 10     &&     toUp()    ) ){ 
                // GO LEFT
                var obj = PopObjectAt_FindAtXY(_borderCase, ActualCase._x-1, ActualCase._y );
                if( obj != null ){
                    newBorder.push( obj );
                    PrevCase = ActualCase;
                    ActualCase = obj;
                    clockwiseEnd = false;
                    
                    // re push case
                    _borderCase.push( PrevCase );
                }
            } else if( (ActualCase._value == 10     &&     toDown()    ) ){ 
                // GO RIGHT
                var obj = PopObjectAt_FindAtXY(_borderCase, ActualCase._x+1, ActualCase._y );
                if( obj != null ){
                    newBorder.push( obj );
                    PrevCase = ActualCase;
                    ActualCase = obj;
                    clockwiseEnd = false;
                    
                    // re push case
                    _borderCase.push( PrevCase );
                }
            }
        } // while(!clockwiseEnd)
        
        
        _borders.push( newBorder );
    }

    
    //console.log( _borders.length );
    return _borders;
};
